/*
 *  FILE: open.c
 *  AUTH: mcc | jal
 *  DESC:
 *  DATE: Mon Apr  6 19:27:49 1998
 */

#include "globals.h"
#include "errno.h"
#include "fs/fcntl.h"
#include "util/string.h"
#include "util/printf.h"
#include "fs/vfs.h"
#include "fs/vnode.h"
#include "fs/file.h"
#include "fs/vfs_syscall.h"
#include "fs/open.h"
#include "fs/stat.h"
#include "util/debug.h"

/* find empty index in p->p_files[] */
int
get_empty_fd(proc_t *p)
{
        int fd;

        for (fd = 0; fd < NFILES; fd++) {
                if (!p->p_files[fd])
                        return fd;
        }

        dbg(DBG_ERROR | DBG_VFS, "ERROR: get_empty_fd: out of file descriptors "
            "for pid %d\n", curproc->p_pid);
        return -EMFILE;
}

/*
 * There a number of steps to opening a file:
 *      1. Get the next empty file descriptor.(check)
 *      2. Call fget to get a fresh file_t.(check)
 *      3. Save the file_t in curproc's file descriptor table.(check)
 *      4. Set file_t->f_mode to OR of FMODE_(READ|WRITE|APPEND) based on (check)(not sure if should consider other combinations)
 *         oflags, which can be O_RDONLY, O_WRONLY or O_RDWR, possibly OR'd with
 *         O_APPEND.
 *      5. Use open_namev() to get the vnode for the file_t.(check)
 *      6. Fill in the fields of the file_t.(check)
 *      7. Return new fd.(check)
 *
 * If anything goes wrong at any point (specifically if the call to open_namev (check)
 * fails), be sure to remove the fd from curproc, fput the file_t and return an
 * error.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EINVAL (UNCHECK)(not sure if should consider other combinations)
 *        oflags is not valid.
 *      o EMFILE (check)
 *        The process already has the maximum number of files open.
 *      o ENOMEM (check)
 *        Insufficient kernel memory was available.
 *      o ENAMETOOLONG (check)
 *        A component of filename was too long.
 *      o ENOENT (check) 
 *        O_CREAT is not set and the named file does not exist.  Or, a
 *        directory component in pathname does not exist.
 *      o EISDIR (check)
 *        pathname refers to a directory and the access requested involved
 *        writing (that is, O_WRONLY or O_RDWR is set).
 *      o ENXIO (check)
 *        pathname refers to a device special file and no corresponding device
 *        exists.
 */

int
do_open(const char *filename, int oflags)
{
	dbgq(DBG_SCHED,"TRYING TO OPEN FILE  %s \n",filename);		
	int status;	
	file_t *f;	
	if(strcmp(filename,"")==0)
	{
		dbgq(DBG_SCHED,"FILE NAME IS EMPTY. ERROR NO. %d \n",-EINVAL);			
		return -EINVAL;
	}
	char teststr[MAXPATHLEN+1];
	strcpy(teststr, filename);
	char * pch;
	pch = strtok (teststr,"/");

	while (pch != NULL) 
	{
		if(strlen(pch)>NAME_LEN)
		{
		dbgq(DBG_SCHED,"FILE NAME IS TOO LONG. ERROR NO. %d \n",-ENAMETOOLONG);
		return -ENAMETOOLONG;  
		}		
		pch = strtok (NULL, "/");
	}



	int fd = get_empty_fd(curproc);
dbgq(DBG_SCHED,"OPEN 2\n");		
	if(fd!=-EMFILE)
	{
		/* can fail if memory not allocated */
		f = fget(-1); 
		if(f==NULL)
		{			
			dbgq(DBG_SCHED,"NO MEMORY. ERROR NO. %d \n",-ENOMEM);			
			return -ENOMEM;
		}
dbgq(DBG_SCHED,"OPEN 3\n");		
		curproc->p_files[fd] = f;

		status = 0;
		if(oflags == (O_WRONLY | O_APPEND))		
		{
			f->f_mode = FMODE_WRITE|FMODE_APPEND;
			status++;
		}
		else if(oflags == (O_RDWR|O_APPEND))		
		{
			f->f_mode = FMODE_READ|FMODE_WRITE|FMODE_APPEND;
			status++;
		}
		else if(oflags == O_RDONLY)		
		{
			f->f_mode = FMODE_READ;
			f->f_pos=0;
			status++;
		}
		else if(oflags == O_WRONLY)		
		{
			f->f_mode = FMODE_WRITE;
			f->f_pos=0;
			status++;
		}
		else if(oflags == O_RDWR)		
		{
			f->f_mode = FMODE_READ | FMODE_WRITE;
			f->f_pos=0;
			status++;
		}
		else if(oflags == (O_RDONLY|O_CREAT) )		
		{
			f->f_mode = FMODE_READ;
			f->f_pos=0;
			status++;
		}
		else if(oflags == (O_WRONLY|O_CREAT) )		
		{
			f->f_mode = FMODE_WRITE;
			f->f_pos=0;
			status++;
		}
		else if(oflags == (O_RDWR|O_CREAT) )		
		{
			f->f_mode = FMODE_READ|FMODE_WRITE;
			f->f_pos=0;
			status++;
		}
		else if(oflags == (O_WRONLY|O_CREAT|O_APPEND) )		
		{
			f->f_mode = FMODE_WRITE;
			status++;
		}
		else if(oflags == (O_RDWR|O_CREAT|O_APPEND) )		
		{
			f->f_mode = FMODE_READ|FMODE_WRITE;
			status++;
		}
		else if(oflags == (O_WRONLY|O_CREAT|O_TRUNC) )		
		{
			f->f_mode = FMODE_WRITE;
			f->f_pos=0;
			status++;
		}
		else if(oflags == (O_RDWR|O_CREAT|O_TRUNC) )		
		{
			f->f_mode = FMODE_READ|FMODE_WRITE;
			f->f_pos=0;
			status++;
		}
		else
		{
			dbgq(DBG_SCHED,"INCORRECT FLAG. ERROR NO. %d \n",-EINVAL);			
			curproc->p_files[fd] = NULL;
dbgq(DBG_SCHED,"OPEN aaa\n");		
			fput(f);			
			return -EINVAL;
		}		
		dbgq(DBG_SCHED,"OPEN 5\n");		
		status = 0;
		
		/* returns int */ 

	     		dbgq(DBG_SCHED,"OPEN 6\n");
             status = open_namev(filename, oflags, &(f->f_vnode), NULL); 
dbgq(DBG_SCHED,"OPEN 7\n");
		if(status<0)
		{
			
			dbgq(DBG_SCHED,"ERROR WHILE OPENING FILE. DOESNOT EXIST.OPEN NAME V RETURNS. %d \n",status);
			curproc->p_files[fd] = NULL;

			fput(f);
			return status;  /* EXPECTED ENOTDIR IN ONE TEST CASE */
		}	
		
				
		if( S_ISDIR(f->f_vnode->vn_mode) && ( ((O_WRONLY & oflags)==O_WRONLY) || ((O_RDWR & oflags)==O_RDWR) ) )
		{
			dbgq(DBG_SCHED,"FILE NAME IS DIRECTORY. ERROR NO. %d \n",-EISDIR);			
			curproc->p_files[fd] = NULL; 
			fput(f);	
				
			return -EISDIR;
		}
	
		if(  (S_ISCHR(f->f_vnode->vn_mode) && !f->f_vnode->vn_cdev) || (S_ISBLK(f->f_vnode->vn_mode) && !f->f_vnode->vn_bdev)  )
		{
			
			dbgq(DBG_SCHED,"FILE NAME IS A SPECIAL DEVICE FILE and no corresponding device exist. %d \n",-ENXIO);			
			curproc->p_files[fd] = NULL;
			fput(f);
			return -ENXIO;
		}
		
		/* 3rd argument SEEK_SET = 0 is not defined, not sure to do this as lseek.h not included 
		status = do_lseek(fd,0,0); 
		if(status<0)
		{
			curproc->p_files[fd] = NULL;
			fput(f);			
			return status;
		}*/
		dbgq(DBG_SCHED,"File %s SUCESSFULLY OPENED \n",filename);
		return fd;
	}
	else
	{
		dbgq(DBG_SCHED,"PROCESS HAS MAX NUMBER OF FILES OPEN. ERROR NO. %d \n",-EMFILE);		
		return -EMFILE;
	
	}
	/*NOT_YET_IMPLEMENTED("VFS: do_open");
        return -1;*/
}
